package com.ggshily.swf.abc;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

public class AVM2Instruction
{
	public static final int LEVEL_ACTION_SCRIPT = 0;
	public static final int LEVEL_BYTECODE = 1;
	public static final int LEVEL_BYTECODE_BINARY = 2;
	
	public static final int[] OPERATIONS = {0x24, 0xd5, 0x73, };
	public static final Class<?>[] OPERATION_CLASSES = {OperationPushByte.class, OperationSetLocal1.class, OperationConvert_i.class};
	
	public static Map<Integer, Class<?>> OPERATION_MAP;
	
	static
	{
		OPERATION_MAP = new HashMap<Integer, Class<?>>();
		for(int i = 0; i < OPERATIONS.length; i++)
		{
			OPERATION_MAP.put(OPERATIONS[i], OPERATION_CLASSES[i]);
		}
	}
	
	public static String getSourceCode(ABCMethodBody methodBody, int leve)
	{
		Object[] registers = new Object[methodBody.localCount];
		Stack<Object> stack = new Stack<Object>();
		
		short[] code = methodBody.code;
		
		StringBuilder sb = new StringBuilder();
		
		int offset = 0;
		while(offset < code.length)
		{
			IABCOperation operation = getOperation(code[offset++] & 0xFF);
			if (operation != null)
			{
				offset = operation.readOperation(code, offset, registers, stack);
				sb.append(operation.getSourceCode(leve));
			}
		}
		return sb.toString();
	}
	
	public static IABCOperation getOperation(int type)
	{
		if(OPERATION_MAP.containsKey(type))
		{
			try
			{
				return (IABCOperation) OPERATION_MAP.get(type).newInstance();
			}
			catch (InstantiationException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IllegalAccessException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return null;
	}
	
	
}

class OperationDebugFile implements IABCOperation
{
	private int value;

	@Override
	public String getSourceCode(int leve)
	{
		return "_as3_debugfile:" + value;
	}

	@Override
	public int readOperation(short[] code, int offset, Object[] registers,
			Stack<Object> stack)
	{
		value = code[offset++] & 0xFF;
		return offset;
	}
	
}

class OperationAdd implements IABCOperation
{

	@Override
	public String getSourceCode(int leve)
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int readOperation(short[] data, int offset, Object[] registers, Stack<Object> stack)
	{
		// TODO Auto-generated method stub
		return 0;
	}
}

class OperationConvert_i implements IABCOperation
{

	@Override
	public String getSourceCode(int leve)
	{
		return "";
	}

	@Override
	public int readOperation(short[] code, int offset, Object[] registers,
			Stack<Object> stack)
	{
		stack.push(new Integer((Short)stack.pop()));
		return offset++;
	}
	
}

class OperationPushByte implements IABCOperation
{

	@Override
	public String getSourceCode(int leve)
	{
		return "";
	}

	@Override
	public int readOperation(short[] code, int offset, Object[] registers,
			Stack<Object> stack)
	{
		stack.push(code[offset++]);
		return offset;
	}
	
}

class OperationGetLocal_1 implements IABCOperation
{

	@Override
	public String getSourceCode(int leve)
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int readOperation(short[] code, int offset, Object[] registers,
			Stack<Object> stack)
	{
		
		return 0;
	}
	
}

class OperationSetLocal1 implements IABCOperation
{
	private boolean isNew;
	private Object value;
	private String valueType;

	@Override
	public String getSourceCode(int leve)
	{
		return (isNew ? "var " : "") + "_loc_1:" + valueType + " = " + value + ";";
	}

	@Override
	public int readOperation(short[] code, int offset, Object[] registers,
			Stack<Object> stack)
	{
		if(registers[1] == null)
		{
			isNew = true;
		}
		value = stack.pop();
		
		if(value.getClass().getSimpleName().equals("Integer"))
		{
			valueType = "int";
		}
		else
		{
			valueType = "*";
		}
		
		return offset++;
	}
	
}